using AvaloniaMaidVSIX.Helpers;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Task = System.Threading.Tasks.Task;

namespace AvaloniaMaidVSIX.Commands
{
    /// <summary>
    /// Command handler
    /// </summary>
    internal sealed class GetXmlnsCommand
    {
        /// <summary>
        /// Command ID.
        /// </summary>
        public const int CommandId = 4129;

        /// <summary>
        /// Command menu group (command set GUID).
        /// </summary>
        public static readonly Guid CommandSet = new Guid("8fa4f937-c539-4fb0-9b70-5474eb8eaaca");

        /// <summary>
        /// VS Package that provides this command, not null.
        /// </summary>
        private readonly AsyncPackage package;

        /// <summary>
        /// Initializes a new instance of the <see cref="GetXmlnsCommand"/> class.
        /// Adds our command handlers for menu (commands must exist in the command table file)
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        /// <param name="commandService">Command service to add command to, not null.</param>
        private GetXmlnsCommand(AsyncPackage package, OleMenuCommandService commandService)
        {
            this.package = package ?? throw new ArgumentNullException(nameof(package));
            commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));

            var menuCommandID = new CommandID(CommandSet, CommandId);
            var menuItem = new MenuCommand(this.Execute, menuCommandID);
            commandService.AddCommand(menuItem);
        }

        /// <summary>
        /// Gets the instance of the command.
        /// </summary>
        public static GetXmlnsCommand Instance
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the service provider from the owner package.
        /// </summary>
        private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider
        {
            get
            {
                return this.package;
            }
        }

        /// <summary>
        /// Initializes the singleton instance of the command.
        /// </summary>
        /// <param name="package">Owner package, not null.</param>
        public static async Task InitializeAsync(AsyncPackage package)
        {
            // Switch to the main thread - the call to AddCommand in GetXmlnsCommand's constructor requires
            // the UI thread.
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);

            OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
            Instance = new GetXmlnsCommand(package, commandService);
        }

        /// <summary>
        /// This function is the callback used to execute the command when the menu item is clicked.
        /// See the constructor to see how the menu item is associated with this function using
        /// OleMenuCommandService service and MenuCommand class.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void Execute(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            var dte =  ServiceProvider.GetServiceAsync(typeof(DTE)).GetAwaiter().GetResult() as DTE2;

            var projectInfo = VSHelper.GetSelectedItemInfoInSolutionExplorerCs(dte);

            try
            {
                if (projectInfo is null) throw new Exception("无法找到当前选中的代码项");
                var namespaces = GetNamespacesFromFile(projectInfo.ItemFullPath);
                if (namespaces is null || namespaces.Count == 0) throw new Exception("无法提取出有效命名空间");

                var namespacesDistinct = namespaces.Distinct();
                var sb = new StringBuilder();
                foreach (var namespaceName in namespacesDistinct)
                {
                    var usingCode = CreateNamespaceUsing(namespaceName);
                    sb.AppendLine(usingCode);
                }
                Clipboard.SetText(sb.ToString());
            }
            catch (Exception ex)
            {
                if (projectInfo is null)
                {
                    VsShellUtilities.ShowMessageBox(
                        this.package,
                        $"遇到错误：{ex.Message}",
                        "提取失效",
                        OLEMSGICON.OLEMSGICON_WARNING,
                        OLEMSGBUTTON.OLEMSGBUTTON_OK,
                        OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
                }
            }


        }

        #region impls

        private static List<string> GetNamespacesFromFile(string file)
        {
            using (var fs = File.OpenRead(file))
            {
                using (var reader = new StreamReader(fs))
                {
                    return GetNamespacesFromStringReader(reader);
                }
            }
        }
        private static List<string> GetNamespacesFromStringReader(StreamReader reader)
        {
            var result = new List<string>();
            while (reader.EndOfStream == false)
            {
                var line = reader.ReadLine();
                if (IsNamespaceDeclare(line))
                {
                    var namespaceName = ExtractNamespaceNameFromDeclare(line);
                    result.Add(namespaceName);
                }
            }

            return result;
        }
        private static bool IsNamespaceDeclare(string code)
        {
            if (string.IsNullOrWhiteSpace(code)) return false;
            if (code.StartsWith("namespace")) return true;
            return false;
        }
        private static string ExtractNamespaceNameFromDeclare(string code)
        {
            return code.Replace(";", string.Empty).Split().Last();
        }

        private static string CreateNamespaceUsing(string namespaceName)
        {
            var shortName = NamespaceHelper.GetNamespaceShortName(namespaceName);
            return $"xmlns:{shortName}=\"using:{namespaceName}\"";
        }


        #endregion
    }
}
